/**
 * Design By: CEC GienTech Technology Co.,Ltd. All rights reserved.
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.ccdc.cbpc.bigdata.base.config;

import com.ccdc.cbpc.bigdata.open.common.enums.RespCodeEnum;
import com.ccdc.cbpc.bigdata.open.common.exception.BusinessException;
import com.ccdc.cbpc.bigdata.open.common.vo.BaseRepVO;
import com.netflix.client.ClientException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.TypeMismatchException;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.dao.DataAccessException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.method.HandlerMethod;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
 * 请尽可能详细的描述定义此类的目的、提供的功能及使用建议。
 * @title com.pactera.bi.app.config.ErrorControllerAdvice
 * @description ErrorControllerAdvice
 * @author <a href="kido@pactera.com">kido</a>
 * @date 2021/7/16
 * @since 1.0.0
 */
@ControllerAdvice
@RestController
@Slf4j
public class ErrorControllerAdvice implements ErrorController {

    /**
     * 接管 /error 统一处理系统error 一般发生在页面错误及资源不足的情况，这时候需要识别http status作为错误信息
     * 按异常类型接管系统异常，对于404等异常，并不会接管，也不会进入该方法内（被系统处理至BasicErrorController 返回 getErrorPath()页面）
     * @param e 捕获的异常
     * @param response
     * @param request
     * @param session
     * @param handler 处理本次请求的handler
     * @return
     */
    @ExceptionHandler(Throwable.class)
    @RequestMapping("/error")
    public BaseRepVO throwableHandler(Throwable e, HttpServletResponse response, HttpServletRequest request, HttpSession session, HandlerMethod handler) {
        // 获取servlet路径
        String servletPath = request.getServletPath();
        // error请求下 e、handler已不能使用，request里的信息已不是原始的请求信息了
        if ("/error".equals(servletPath)) {
            servletPath = null;
            handler = null;
            e = null;
        }
        log.info("发生错误在请求:"+servletPath);
        return getRespVoByThrowable(e, response,handler);
    }

    @Override
    public String getErrorPath() {
        return "/error";
    }
    /**
     * 根据异常类型确定返回数据的内容
     * @param e
     * @param response
     * @param handler
     * @return
     */
    public BaseRepVO getRespVoByThrowable(Throwable e, HttpServletResponse response, HandlerMethod handler) {
        // 未找到处理handler
        if (handler == null) {
            return new BaseRepVO(RespCodeEnum.NOT_FOUND);
        }
        // 根据异常信息转换
        if(e != null) {
            //输出异常信息
            log.error("统一异常处理",e);
            if (e instanceof BusinessException) {
                // 自定义异常直接读取异常信息
                return new BaseRepVO((BusinessException) e);
            }else if(isClientException(e)) {
                // Cloud 调用异常
                return new BaseRepVO(RespCodeEnum.SERVER_UNAVAILABLE);
            }else if(e instanceof TypeMismatchException) {
                return new BaseRepVO(RespCodeEnum.NOT_ALLOWED);
            }else if(e instanceof DataAccessException) {
                return new BaseRepVO(RespCodeEnum.SERVER_DATA_ERROR);
            }
        }
        return new BaseRepVO(RespCodeEnum.SERVER_ERROR);
    }

    private boolean isClientException(Throwable e) {
        if (e instanceof ClientException) {
            return true;
        }
        while (e.getCause() != null) {
            e = e.getCause();
            if (e instanceof ClientException) {
                return true;
            }
        }
        return false;
    }
}
